package com.example.demo.Service;

import com.example.demo.domain.Student;
import com.example.demo.domain.User;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCollapser;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import com.netflix.hystrix.contrib.javanica.cache.annotation.CacheKey;
import com.netflix.hystrix.contrib.javanica.cache.annotation.CacheResult;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.client.RestTemplate;

import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Future;

@Service("studentService")
public class StudentService {


    private Logger log = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private RestTemplate restTemplate;

    /**
     * 合并请求
     * @HystrixCollapser注解的batchMethod属性指定了批量处理的方法为下面定义的findUserBatch方法，
     * timerDelayInMilliseconds的值为100（毫秒），意思是在100毫秒这个时间范围内的所有对findUser的调用，
     * 都将被合并为一个批量处理操作，进行批量处理操作的方法就是findUserBatch。
     * @param id
     * @return
     */
    @HystrixCollapser(batchMethod = "findUserBatch", collapserProperties = {
            @HystrixProperty(name = "timerDelayInMilliseconds", value = "100")
    })
    public Future<Student> findUser(Long id) {
        log.info("获取单个用户信息");
        // return new AsyncResult<User>() {
        //     @Override
        //     public User invoke() {
        //         return restTemplate.getForObject("http://Server-Provider/user/{id}", User.class, id);
        //     }
        // };
        return null;
    }

    @HystrixCommand
    public List<Student> findUserBatch(List<Long> ids) {
        log.info("批量获取用户信息,ids: " + ids);
        Student[] users = restTemplate.getForObject("http://Server-Provider/student/users?ids={1}", Student[].class, StringUtils.join(ids, ","));
        return Arrays.asList(users);
    }

    /**
     * 加入了@HystrixCommand注解，注解的fallbackMethod属性指定了被调用的方法不可用时的回调方法（服务熔断时的回调处理逻辑，即服务降级），
     * 这里为getUserDefault方法（必须与getUser方法的参数及返回值类型一致）。
     * @CacheResult 开启缓存
     * @param id
     * @return
     */
    @CacheResult(cacheKeyMethod = "getCacheKey")//@CacheKey("id"),cacheKeyMethod = "getCacheKey"两种指定缓存的key值
    @HystrixCommand(fallbackMethod = "getUserDefault")
    public Student getUser(@PathVariable Long id) {
        log.info("获取学生信息: " + id);
        return restTemplate.getForObject("http://Server-Provider/student/{id}", Student.class, id);
    }

    public String getCacheKey(Long id) {
        return String.valueOf(id);
    }

    /**
     * 如果getUserDefault方法也抛出异常，那么我们可以再次使用@HystrixCommand注解指定getUserDefault方法降级的方法
     * ignoreExceptions: 除了ignoreExceptions忽略的异常，其他异常都触发服务降级
     * @param e 可以获取到getUser方法抛出的异常
     * @param id
     * @return
     * ignoreExceptions = {NullPointerException.class}
     */
    @HystrixCommand(fallbackMethod = "getUserDefault2",ignoreExceptions = {NullPointerException.class})
    public Student getUserDefault(Long id,Throwable e) {
        String a = null;
        // 测试服务降级
//        a.toString();
        System.out.println(e.getMessage());
        Student user = new Student();
        user.setId(-1L);
        user.setName("defaultUser");
        user.setPassword("123456");
        return user;
    }

    public Student getUserDefault2(Long id) {
        Student user = new Student();
        user.setId(-2L);
        user.setName("defaultUser2");
        user.setPassword("123456");
        return user;
    }

    public List<Student> getUsers() {
        return this.restTemplate.getForObject("http://Server-Provider/student", List.class);
    }

    public String addUser() {
        Student user = new Student(1L, "mrbird", "123456");
        HttpStatus status = this.restTemplate.postForEntity("http://Server-Provider/student", user, null).getStatusCode();
        if (status.is2xxSuccessful()) {
            return "新增用户成功";
        } else {
            return "新增用户失败";
        }
    }

    public void updateUser(Student student) {
        this.restTemplate.put("http://Server-Provider/student", student);
    }

    public void deleteUser(@PathVariable Long id) {
        this.restTemplate.delete("http://Server-Provider/student/{1}", id);
    }
}
